package com.ruoyi.rulengine.service.impl;

import com.alibaba.fastjson.JSON;
import com.ruoyi.iotcommon.domain.DeviceDataMessage;
import com.ruoyi.rulengine.domain.Rule;
import com.ruoyi.rulengine.domain.RuleAction;
import com.ruoyi.rulengine.domain.RuleCondition;
import com.ruoyi.rulengine.domain.RuleLog;
import com.ruoyi.rulengine.domain.emun.RuleConstant;
import com.ruoyi.rulengine.evaluator.RuleConditionEvaluator;
import com.ruoyi.rulengine.handler.RuleActionFactory;
import com.ruoyi.rulengine.service.IRuleEngineService;
import com.ruoyi.rulengine.service.IRuleLogService;
import com.ruoyi.rulengine.service.IRuleService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
@Service("ruleEngineService")
public class RuleEngineServiceImpl implements IRuleEngineService {

    // 缓存规则，每10分钟从数据库同步一次
    public static final Map<String,List<Rule>> deviceRulesCache = new ConcurrentHashMap<>();

    @Resource
    ThreadPoolTaskExecutor taskExecutor;

    @Resource
    RuleConditionEvaluator evaluator;

    @Resource
    RuleActionFactory ruleActionFactory;

    @Resource
    IRuleLogService ruleLogService;

    @Resource
    IRuleService ruleService;


    public CompletableFuture<Void> evaluateRules(List<Rule> rules, DeviceDataMessage deviceData) {

        List<CompletableFuture<Void>> futures = rules.stream()
                .map(rule -> processSingleRule(rule, deviceData))
                .toList();

        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .exceptionally(ex -> {
                    log.error("Batch rule processing failed", ex);
                    return null;
                });
    }

    private CompletableFuture<Void> processSingleRule(Rule rule, DeviceDataMessage deviceData) {
        RuleLog ruleLog = createRuleLog(rule, deviceData, "PENDING");
        rule.setRuleLog(ruleLog);

        return evaluateConditions(rule, rule.getLogical(), deviceData)
                .thenAcceptAsync(matchResult -> {
                    // 满足所有条件时，执行动作并记录日志
                    if (Boolean.TRUE.equals(matchResult)) {
                        executeActions(rule, deviceData);
                        ruleLogService.insertRuleLog(ruleLog);
                    }
                }, taskExecutor)
                .exceptionally(ex -> {
                    log.error("Rule processing failed: ruleId={}, deviceKey={}", 
                            rule.getId(), deviceData.getDeviceKey(), ex);
                    return null;
                });
    }

    public CompletableFuture<Boolean> evaluateConditions(Rule rule, String logic, DeviceDataMessage deviceData) {
        if (rule == null || CollectionUtils.isEmpty(rule.getConditions())) {
            return  CompletableFuture.completedFuture(false);
        }

        List<CompletableFuture<RuleCondition>> conditionFutures = rule.getConditions().stream()
                .map(condition -> evaluateSingleCondition(condition, deviceData))
                .toList();

        return CompletableFuture.allOf(conditionFutures.toArray(new CompletableFuture[0]))
        .thenApplyAsync(v -> {
            List<RuleCondition> evaluatedConditions = conditionFutures.stream()
                    .map(CompletableFuture::join)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            
            String description = buildConditionDescription(evaluatedConditions, deviceData);
            rule.getRuleLog().setDes(description);
            return applyLogicToConditions(evaluatedConditions, logic, rule.getId());
        }, taskExecutor)
        .exceptionally(ex -> {
            log.error("Condition evaluation failed: ruleId={}, deviceKey={}", 
                    rule.getId(), deviceData.getDeviceKey(), ex);
            return false;
        });
    }

    private CompletableFuture<RuleCondition> evaluateSingleCondition(RuleCondition condition, DeviceDataMessage deviceData) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return evaluator.evaluate(condition, deviceData);
            } catch (Exception e) {
                log.error("Condition evaluation error: conditionId={}", condition.getId(), e);
                condition.setIsMatched(false);
                return condition;
            }
        }, taskExecutor);
    }

    private String buildConditionDescription(List<RuleCondition> conditions,  DeviceDataMessage deviceData) {
        return conditions.stream()
                .filter(RuleCondition::getIsMatched)
                .map(condition -> String.format("%s(%s) %s %s;",
                        condition.getPropertyKey(),
                        deviceData.getParams().get(condition.getPropertyKey()),
                        condition.getOperatorType(),
                        condition.getPropertyValue()))
                .collect(Collectors.joining(" "));
    }

    private boolean applyLogicToConditions(List<RuleCondition> conditions, String logic, Long ruleId) {
        boolean result = switch (logic) {
            case RuleConstant.LOGIC_AND -> conditions.stream().allMatch(RuleCondition::getIsMatched);
            case RuleConstant.LOGIC_OR -> conditions.stream().anyMatch(RuleCondition::getIsMatched);
            default -> throw new IllegalArgumentException("Unsupported logic type: " + logic);
        };
        
        log.debug("Logic evaluated: ruleId={}, logic={}, result={}", ruleId, logic, result);
        return result;
    }



    @Override
    public void executeActions(Rule rule, DeviceDataMessage deviceData) {
        List<RuleAction> actions = rule.getActions();
        if (CollectionUtils.isEmpty(actions)) {
            return;
        }
        RuleLog ruleLog = rule.getRuleLog();
        Map<String, String> actionResults = new HashMap<>();
        CompletableFuture<?>[] futures = actions.stream()
                .map(action -> CompletableFuture.runAsync(() -> {
                    try {
                        // 执行动作，并获取执行结果
                        RuleAction result = ruleActionFactory.getHandler(action).execute(action);
                        log.info("执行动作: " + action.getType());
                        actionResults.put(action.getType(),  result.getResult()); // 记录执行结果
                    } catch (Exception e) {
                        actionResults.put(action.getType(),  e.getMessage()); // 记录失败
                    }
                }, taskExecutor))
                .toArray(CompletableFuture[]::new);

        // 等待所有动作执行完成
        CompletableFuture.allOf(futures).join();

        // 将动作执行结果设置到 RuleLog 中
        ruleLog.setActionResult(JSON.toJSONString(actionResults));
        // 根据动作执行结果更新 RuleLog 的状态
        ruleLog.setActionStatus(actionResults.values().stream()
        .anyMatch(result -> result.startsWith(RuleConstant.ACTION_FAIL)) 
        ? RuleConstant.ACTION_FAIL 
        : RuleConstant.ACTION_SUCCESS);
    }

    @Override
    public RuleLog createRuleLog(Rule rule, DeviceDataMessage deviceData, String status) {
        return RuleLog.builder()
                .ruleId(rule.getId())
                .ruleName(rule.getName())
                .level(rule.getLevel())
                .createTime(new Date())
                .productKey(deviceData.getProductKey())
                .deviceKey(deviceData.getDeviceKey())
                .channelKey(deviceData.getChannelKey())
                .actionStatus(status)
                .build();
    }


    @Override
    public void getRules(DeviceDataMessage deviceData) {
        CompletableFuture.runAsync(() -> {
            // 从数据库查询规则列表
            List<Rule> rules = ruleService.selectRuleListByDevice(deviceData.getProductKey(), deviceData.getChannelKey(), deviceData.getDeviceKey());
            //List<Rule> rules = deviceRulesCache.get(deviceData.getProductKey()+deviceData.getChannelKey()+deviceData.getDeviceKey());
            if(CollectionUtils.isEmpty(rules)){
                log.info("未找到符合条件的规则条件，productKey={}, channel={}, deviceKey={}", deviceData.getProductKey(), deviceData.getChannelKey(), deviceData.getDeviceKey());
                return;
            }
            evaluateRules(rules, deviceData);
        }, taskExecutor);

    }
}
